using System;
using System.Collections.Generic;
using System.Linq;
using AndyPike.Commons.Persistence;
using AndyPike.Commons.Persistence.InMemory;
using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;
using Rhino.Mocks;

namespace AndyPike.Commons.Tests.Unit.Persistence.InMemory
{
    [TestFixture]
    public class When_using_the_in_memory_repository
    {
        private MockRepository mockery;
        private IRepository<EntityStub> repository;
        private IList<EntityStub> entities;
        private Guid id;

        [SetUp]
        public void SetUp()
        {
            mockery = new MockRepository();
            id = Guid.NewGuid();
            entities = new List<EntityStub>
                           {
                               new EntityStub{Id = Guid.NewGuid()},
                               new EntityStub{Id = id},
                               new EntityStub{Id = Guid.NewGuid()},
                               new EntityStub{Id = Guid.NewGuid()}
                           };
            repository = new InMemoryRepository<EntityStub>(entities);
        }

        [Test]
        public void Should_add_an_entity_to_the_collection_when_saving()
        {
            repository.Save(new EntityStub{Id = Guid.NewGuid()});

            Assert.That(entities.Count, Is.EqualTo(5));
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void Should_throw_an_exception_if_null_is_passed_in_while_saving()
        {
            repository.Save(null);
        }

        [Test]
        public void Should_find_an_entity_by_id()
        {
            EntityStub entity = repository.FindById(id);

            Assert.That(entity.Id, Is.EqualTo(id));
        }

        [Test]
        [ExpectedException(typeof(ArgumentException))]
        public void Should_throw_an_exception_if_an_empty_id_is_passed_into_FindById()
        {
            repository.FindById(Guid.Empty);
        }

        [Test]
        public void Should_return_a_queryable_version_of_the_entities()
        {
            IQueryable<EntityStub> queryable = repository.AsQueryable();

            Assert.That(queryable, Is.Not.Null);
            Assert.That(queryable.ToList().Count, Is.EqualTo(4));
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void Should_throw_an_exception_if_null_is_passed_into_Find()
        {
            repository.Find(null);
        }

        [Test]
        public void Should_execute_the_passed_query_command_when_calling_Find()
        {
            ICollection<EntityStub> matches = null;
            var query = mockery.DynamicMock<IQueryCommand<EntityStub>>();

            With.Mocks(mockery)
                .Expecting(() => Expect.Call(query.Execute(repository)).Return(entities.AsQueryable()))
                .Verify(() => matches = repository.Find(query));

            Assert.That(matches.Count, Is.EqualTo(4));
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void Should_throw_an_exception_if_null_is_passed_into_FindOne()
        {
            repository.FindOne(null);
        }

        [Test]
        public void Should_execute_the_passed_query_command_when_calling_FindOne()
        {
            EntityStub match = null;
            var query = mockery.DynamicMock<IQueryCommand<EntityStub>>();

            With.Mocks(mockery)
                .Expecting(() => Expect.Call(query.Execute(repository)).Return(entities.AsQueryable()))
                .Verify(() => match = repository.FindOne(query));

            Assert.That(match, Is.Not.Null);
        }
    }

    public class EntityStub : IEntity
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
    }
}